<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>Three.js 天气模拟</title>
		<style>
			body {
				margin: 0;
				overflow: hidden;
				font-family: Arial, sans-serif;
			}
			#info {
				position: absolute;
				top: 10px;
				width: 100%;
				text-align: center;
				color: white;
				background-color: rgba(0, 0, 0, 0.5);
				padding: 10px;
				z-index: 100;
			}
			#controls {
				position: absolute;
				bottom: 20px;
				left: 50%;
				transform: translateX(-50%);
				z-index: 100;
				display: flex;
				gap: 10px;
			}
			button {
				padding: 10px 15px;
				background-color: rgba(255, 255, 255, 0.8);
				border: none;
				border-radius: 5px;
				cursor: pointer;
				font-weight: bold;
			}
			button:hover {
				background-color: white;
			}
		</style>
	</head>
	<body>
		<div id="info">Three.js 天气模拟 - 当前天气: 晴天</div>
		<div id="controls">
			<button id="sunny">晴天</button>
			<button id="cloudy">多云</button>
			<button id="overcast">阴天</button>
			<button id="rain">下雨</button>
			<button id="snow">下雪</button>
		</div>
		<div id="tag">标签内容</div>
		<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script>
		<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/controls/OrbitControls.js"></script>
		<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/objects/Sky.js"></script>
		<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/renderers/CSS3DRenderer.js"></script>
		<script>
			document.getElementById('tag').addEventListener('click', function () {
				console.log('标签被点击了');
			});
			let scene, camera, renderer, controls, sky, css3Renderer;

			const initCss3 = () => {
				css3Renderer = new THREE.CSS3DRenderer();
				css3Renderer.setSize(window.innerWidth, window.innerHeight);
				css3Renderer.domElement.style.position = 'absolute';
				css3Renderer.domElement.style.top = '0px';
				css3Renderer.domElement.style.pointerEvents = 'none';
				document.body.appendChild(css3Renderer.domElement);
			};
			const addCss3Dom = (model) => {
				// const element = document.createElement('div');
				const element = document.getElementById('tag');
				element.innerHTML = '标签内容';
				element.style.color = 'white';
				element.style.backgroundColor = 'rgba(0,0,0,0.5)';

				const css3DObject = new THREE.CSS3DObject(element);
				css3DObject.position.set(
					model.position.x,
					model.position.y + 10,
					model.position.z
				); // 设置标签位置
				// scene.add(css3DObject);
				model.add(css3DObject);
			};
			const initThree = () => {
				// 初始化场景、相机和渲染器
				scene = new THREE.Scene();
				camera = new THREE.PerspectiveCamera(
					75,
					window.innerWidth / window.innerHeight,
					0.1,
					10000
				);
				camera.position.set(0, 100, 500);

				renderer = new THREE.WebGLRenderer({ antialias: true });
				renderer.setSize(window.innerWidth, window.innerHeight);
				renderer.shadowMap.enabled = true;
				document.body.appendChild(renderer.domElement);

				// 添加控制器
				controls = new THREE.OrbitControls(camera, renderer.domElement);
				controls.enableDamping = true;
				controls.dampingFactor = 0.05;

				// 添加地面
				const groundGeometry = new THREE.PlaneGeometry(2000, 2000);
				const groundMaterial = new THREE.MeshStandardMaterial({
					color: 0xcccccc,
					roughness: 0.8,
					metalness: 0.2,
				});
				const ground = new THREE.Mesh(groundGeometry, groundMaterial);
				ground.rotation.x = -Math.PI / 2;
				ground.position.y = -0.5;
				ground.receiveShadow = true;
				scene.add(ground);
			};
			initThree();
			initCss3();

			// 添加一些简单的建筑或物体作为参考
			const boxGeometry = new THREE.BoxGeometry(30, 50, 30);
			const boxMaterial = new THREE.MeshStandardMaterial({ color: 0x8888ff });
			const box = new THREE.Mesh(boxGeometry, boxMaterial);
			box.position.set(50, 25, 50);
			box.castShadow = true;
			box.receiveShadow = true;
			addCss3Dom(box);
			scene.add(box);

			const sphereGeometry = new THREE.SphereGeometry(20, 32, 32);
			const sphereMaterial = new THREE.MeshStandardMaterial({
				color: 0xff8888,
			});
			const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
			sphere.position.set(-50, 20, -50);
			sphere.castShadow = true;
			sphere.receiveShadow = true;
			scene.add(sphere);

			// 添加光源
			const ambientLight = new THREE.AmbientLight(0x404040);
			scene.add(ambientLight);

			const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
			directionalLight.position.set(100, 100, 50);
			directionalLight.castShadow = true;
			directionalLight.shadow.mapSize.width = 2048;
			directionalLight.shadow.mapSize.height = 2048;
			scene.add(directionalLight);

			// 添加天空
			sky = new THREE.Sky();
			sky.scale.setScalar(10000);
			scene.add(sky);

			// 天空参数
			const skyUniforms = sky.material.uniforms;
			skyUniforms['turbidity'].value = 2;
			skyUniforms['rayleigh'].value = 3;
			skyUniforms['mieCoefficient'].value = 0.005;
			skyUniforms['mieDirectionalG'].value = 0.8;

			// 太阳位置
			const sun = new THREE.Vector3();
			const sunParams = {
				elevation: 45,
				azimuth: 180,
			};

			const updateSunPosition = () => {
				const phi = THREE.MathUtils.degToRad(90 - sunParams.elevation);
				const theta = THREE.MathUtils.degToRad(sunParams.azimuth);
				sun.setFromSphericalCoords(1, phi, theta);
				skyUniforms['sunPosition'].value.copy(sun);
				directionalLight.position.copy(sun);
			};
			updateSunPosition();

			// 天气效果容器
			const weatherEffects = {
				current: 'sunny',
				rain: null,
				snow: null,
				clouds: null,
			};

			// 创建下雨效果
			function createRain() {
				const rainGeometry = new THREE.BufferGeometry();
				const rainCount = 5000;
				const positions = new Float32Array(rainCount * 3);
				const velocities = new Float32Array(rainCount * 3);

				for (let i = 0; i < rainCount; i++) {
					positions[i * 3] = (Math.random() - 0.5) * 1000;
					positions[i * 3 + 1] = Math.random() * 500 + 100;
					positions[i * 3 + 2] = (Math.random() - 0.5) * 1000;

					velocities[i * 3] = (Math.random() - 0.5) * 0.5;
					velocities[i * 3 + 1] = -5 - Math.random() * 5;
					velocities[i * 3 + 2] = (Math.random() - 0.5) * 0.5;
				}

				rainGeometry.setAttribute(
					'position',
					new THREE.BufferAttribute(positions, 3)
				);
				rainGeometry.setAttribute(
					'velocity',
					new THREE.BufferAttribute(velocities, 3)
				);

				const rainMaterial = new THREE.PointsMaterial({
					size: 2,
					color: 0xaaaaaa,
					transparent: true,
					opacity: 0.8,
					sizeAttenuation: true,
				});

				const rain = new THREE.Points(rainGeometry, rainMaterial);
				rain.name = 'rain';
				return rain;
			}

			// 创建下雪效果
			function createSnow() {
				const snowGeometry = new THREE.BufferGeometry();
				const snowCount = 3000;
				const positions = new Float32Array(snowCount * 3);
				const velocities = new Float32Array(snowCount * 3);
				const rotations = new Float32Array(snowCount * 3);

				for (let i = 0; i < snowCount; i++) {
					positions[i * 3] = (Math.random() - 0.5) * 1000;
					positions[i * 3 + 1] = Math.random() * 500 + 100;
					positions[i * 3 + 2] = (Math.random() - 0.5) * 1000;

					velocities[i * 3] = (Math.random() - 0.5) * 0.5;
					velocities[i * 3 + 1] = -1 - Math.random();
					velocities[i * 3 + 2] = (Math.random() - 0.5) * 0.5;

					rotations[i * 3] = Math.random() * Math.PI * 2;
					rotations[i * 3 + 1] = Math.random() * Math.PI * 2;
					rotations[i * 3 + 2] = Math.random() * Math.PI * 2;
				}

				snowGeometry.setAttribute(
					'position',
					new THREE.BufferAttribute(positions, 3)
				);
				snowGeometry.setAttribute(
					'velocity',
					new THREE.BufferAttribute(velocities, 3)
				);
				snowGeometry.setAttribute(
					'rotation',
					new THREE.BufferAttribute(rotations, 3)
				);

				const snowMaterial = new THREE.PointsMaterial({
					size: 4,
					color: 0xffffff,
					transparent: true,
					opacity: 0.9,
					sizeAttenuation: true,
				});

				const snow = new THREE.Points(snowGeometry, snowMaterial);
				snow.name = 'snow';
				return snow;
			}

			// 创建多云效果
			function createClouds() {
				const cloudGroup = new THREE.Group();
				const cloudGeometry = new THREE.SphereGeometry(30, 8, 8);
				const cloudMaterial = new THREE.MeshLambertMaterial({
					color: 0xffffff,
					transparent: true,
					opacity: 0.8,
				});

				for (let i = 0; i < 20; i++) {
					const cloud = new THREE.Mesh(cloudGeometry, cloudMaterial);
					cloud.position.set(
						(Math.random() - 0.5) * 800,
						Math.random() * 100 + 100,
						(Math.random() - 0.5) * 800
					);
					cloud.scale.set(
						1 + Math.random() * 3,
						0.5 + Math.random(),
						1 + Math.random() * 2
					);
					cloud.castShadow = true;
					cloudGroup.add(cloud);
				}

				cloudGroup.name = 'clouds';
				return cloudGroup;
			}

			// 更新天气效果
			function updateWeather(type) {
				// 移除现有天气效果
				if (weatherEffects.rain) scene.remove(weatherEffects.rain);
				if (weatherEffects.snow) scene.remove(weatherEffects.snow);
				if (weatherEffects.clouds) scene.remove(weatherEffects.clouds);

				// 更新天空和光照
				switch (type) {
					case 'sunny':
						skyUniforms['turbidity'].value = 2;
						skyUniforms['rayleigh'].value = 3;
						skyUniforms['mieCoefficient'].value = 0.005;
						skyUniforms['mieDirectionalG'].value = 0.8;
						sunParams.elevation = 45;
						updateSunPosition();
						directionalLight.intensity = 1;
						ambientLight.intensity = 0.4;
						break;

					case 'cloudy':
						weatherEffects.clouds = createClouds();
						scene.add(weatherEffects.clouds);
						skyUniforms['turbidity'].value = 8;
						skyUniforms['rayleigh'].value = 2;
						skyUniforms['mieCoefficient'].value = 0.02;
						skyUniforms['mieDirectionalG'].value = 0.9;
						sunParams.elevation = 30;
						updateSunPosition();
						directionalLight.intensity = 0.7;
						ambientLight.intensity = 0.5;
						break;

					case 'overcast':
						skyUniforms['turbidity'].value = 15;
						skyUniforms['rayleigh'].value = 0.5;
						skyUniforms['mieCoefficient'].value = 0.2;
						skyUniforms['mieDirectionalG'].value = 0.1;
						sunParams.elevation = 10;
						updateSunPosition();
						directionalLight.intensity = 0.3;
						ambientLight.intensity = 0.3;
						break;

					case 'rain':
						weatherEffects.rain = createRain();
						scene.add(weatherEffects.rain);
						weatherEffects.clouds = createClouds();
						scene.add(weatherEffects.clouds);
						skyUniforms['turbidity'].value = 10;
						skyUniforms['rayleigh'].value = 1.5;
						skyUniforms['mieCoefficient'].value = 0.05;
						skyUniforms['mieDirectionalG'].value = 0.95;
						sunParams.elevation = 15;
						updateSunPosition();
						directionalLight.intensity = 0.2;
						ambientLight.intensity = 0.2;
						break;

					case 'snow':
						weatherEffects.snow = createSnow();
						scene.add(weatherEffects.snow);
						weatherEffects.clouds = createClouds();
						scene.add(weatherEffects.clouds);
						skyUniforms['turbidity'].value = 8;
						skyUniforms['rayleigh'].value = 2;
						skyUniforms['mieCoefficient'].value = 0.03;
						skyUniforms['mieDirectionalG'].value = 0.9;
						sunParams.elevation = 20;
						updateSunPosition();
						directionalLight.intensity = 0.4;
						ambientLight.intensity = 0.4;
						break;
				}

				weatherEffects.current = type;
				document.getElementById(
					'info'
				).textContent = `Three.js 天气模拟 - 当前天气: ${getWeatherName(type)}`;
			}

			function getWeatherName(type) {
				const names = {
					sunny: '晴天',
					cloudy: '多云',
					overcast: '阴天',
					rain: '下雨',
					snow: '下雪',
				};
				return names[type];
			}

			// 动画循环
			function animate() {
				requestAnimationFrame(animate);
				controls.update();

				// 更新下雨效果
				if (weatherEffects.rain) {
					const positions =
						weatherEffects.rain.geometry.attributes.position.array;
					const velocities =
						weatherEffects.rain.geometry.attributes.velocity.array;

					for (let i = 0; i < positions.length; i += 3) {
						positions[i] += velocities[i];
						positions[i + 1] += velocities[i + 1];
						positions[i + 2] += velocities[i + 2];

						if (positions[i + 1] < 0) {
							positions[i] = (Math.random() - 0.5) * 1000;
							positions[i + 1] = Math.random() * 100 + 400;
							positions[i + 2] = (Math.random() - 0.5) * 1000;
						}
					}

					weatherEffects.rain.geometry.attributes.position.needsUpdate = true;
				}

				// 更新下雪效果
				if (weatherEffects.snow) {
					const positions =
						weatherEffects.snow.geometry.attributes.position.array;
					const velocities =
						weatherEffects.snow.geometry.attributes.velocity.array;
					const rotations =
						weatherEffects.snow.geometry.attributes.rotation.array;

					for (let i = 0; i < positions.length; i += 3) {
						positions[i] += velocities[i];
						positions[i + 1] += velocities[i + 1];
						positions[i + 2] += velocities[i + 2];

						rotations[i] += 0.01;
						rotations[i + 1] += 0.01;
						rotations[i + 2] += 0.01;

						if (positions[i + 1] < 0) {
							positions[i] = (Math.random() - 0.5) * 1000;
							positions[i + 1] = Math.random() * 100 + 400;
							positions[i + 2] = (Math.random() - 0.5) * 1000;
						}
					}

					weatherEffects.snow.geometry.attributes.position.needsUpdate = true;
					weatherEffects.snow.geometry.attributes.rotation.needsUpdate = true;
				}

				// 更新云层效果
				if (weatherEffects.clouds) {
					weatherEffects.clouds.children.forEach((cloud) => {
						cloud.position.x += 0.1;
						if (cloud.position.x > 500) {
							cloud.position.x = -500;
						}
					});
				}

				renderer.render(scene, camera);
				css3Renderer.render(scene, camera);
			}

			// 窗口大小调整
			window.addEventListener('resize', () => {
				camera.aspect = window.innerWidth / window.innerHeight;
				camera.updateProjectionMatrix();
				renderer.setSize(window.innerWidth, window.innerHeight);
				css3Renderer.setSize(window.innerWidth, window.innerHeight);
			});

			// 添加按钮事件
			document
				.getElementById('sunny')
				.addEventListener('click', () => updateWeather('sunny'));
			document
				.getElementById('cloudy')
				.addEventListener('click', () => updateWeather('cloudy'));
			document
				.getElementById('overcast')
				.addEventListener('click', () => updateWeather('overcast'));
			document
				.getElementById('rain')
				.addEventListener('click', () => updateWeather('rain'));
			document
				.getElementById('snow')
				.addEventListener('click', () => updateWeather('snow'));

			// 初始化为晴天
			updateWeather('sunny');
			animate();
		</script>
	</body>
</html>
